#ifdef __linux__
#define MAX_BUFFER_SIZE 1024

#include <libserv/libserv.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdlib>
#include <unistd.h>
#include <mutex>
#include <termios.h>
#include <zconf.h>
#include <linux/serial.h>
#include <cstdio>
#include <cstring>
#include <atomic>
#include <queue>
#include <thread>

namespace libserv {
    std::atomic<bool> is_stop;

    int default_print_function(void *arg, std::vector<char> message) {

    }

    struct parameter {
        int fd = -1;
        char head = 0x55;
        int read_buffer_size = MAX_BUFFER_SIZE;
        int id;
        std::function<int((void * , std::vector<char>))> bind_function = default_print_function;
    };


    std::mutex Serial::mu_read_write_port;

    int Serial::tty_num = -1;
    std::vector<libserv::Message *> Serial::messages;

    Serial::Serial() : m_is_run(false) {

    }

    Serial::Serial(std::string serial_device, int frequence, unsigned char head) : head(head), m_is_run(false) {
        this->bind_port(serial_device, frequence);
    }

    int Serial::bind_port(std::string serial_device, int frequence) {
        this->fd = open(serial_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); //O_NDELAY
        if (this->fd < 0) {
            std::cout << "[ Error ] Can't Open Serial " << serial_device << std::endl;
            return -1;
        }
        set_serial(this->fd, frequence, 8, 'N', 1);
        return 0;
    }

    void Serial::bind_package_check(bool (*check_function)(std::vector<char>)) {
        this->check_function = check_function;
    }

    void Serial::bind_call(int(*bind_function)(void *arg, std::vector<char> message)) {
        this->call_function = bind_function;
    }

    int Serial::set_serial(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
        struct termios newttys1, oldttys1;
        if (tcgetattr(this->fd, &oldttys1) != 0) {
            perror("Setupserial 1");
            return -1;
        }
        bzero(&newttys1, sizeof(newttys1));
        newttys1.c_cflag |= (CLOCAL | CREAD);
        newttys1.c_cflag &= ~CSIZE;
        switch (nBits) {
            case 7:
                newttys1.c_cflag |= CS7;
                break;
            case 8:
                newttys1.c_cflag |= CS8;
                break;
        }
        switch (nEvent) {
            case '0':
                newttys1.c_cflag |= PARENB;
                newttys1.c_iflag |= (INPCK | ISTRIP);
                newttys1.c_cflag |= PARODD;
                break;
            case 'E':
                newttys1.c_cflag |= PARENB;
                newttys1.c_iflag |= (INPCK | ISTRIP);
                newttys1.c_cflag &= ~PARODD;
                break;
            case 'N':
                newttys1.c_cflag &= ~PARENB;
                break;
        }
        switch (nSpeed) {
            case 2400:
                cfsetispeed(&newttys1, B2400);
                cfsetospeed(&newttys1, B2400);
                break;
            case 4800:
                cfsetispeed(&newttys1, B4800);
                cfsetospeed(&newttys1, B4800);
                break;
            case 9600:
                cfsetispeed(&newttys1, B9600);
                cfsetospeed(&newttys1, B9600);
                break;
            case 115200:
                cfsetispeed(&newttys1, B115200);
                cfsetospeed(&newttys1, B115200);
                break;
            default:
                cfsetispeed(&newttys1, B9600);
                cfsetospeed(&newttys1, B9600);
                break;
        }
        if (nStop == 1) {
            newttys1.c_cflag &= ~CSTOPB;
        } else if (nStop == 2) {
            newttys1.c_cflag |= CSTOPB;
        }
        newttys1.c_cc[VTIME] = 0;
        newttys1.c_cc[VMIN] = 0;
        tcflush(this->fd, TCIFLUSH);
        if ((tcsetattr(this->fd, TCSANOW, &newttys1)) != 0) {
            perror("com set error");
            return -1;
        }
        return 0;
    }

    std::string get_time() {
        time_t timep;
        time(&timep);
        char tmp[64];
        strftime(tmp, sizeof(tmp), "%Y-%m-%d %H-%M-%S ", localtime(&timep));
        return tmp;
    }

    bool Serial::ansy_send(char *buffer, int buffer_size) {
        if (this->is_open()) {
            std::cout << "[ SEND ] " << get_time();
            for (int i = 0; i < buffer_size; i++) {
                std::cout << "\\x" << std::hex << (short) buffer[i];
            }
            std::cout << std::endl;
            std::unique_lock<std::mutex> mu_read_write_port_lock(mu_read_write_port);//lock
            int wr_num = write(this->fd, buffer, buffer_size);
            mu_read_write_port_lock.unlock();//unlock
            if (wr_num)return 1;
            return 0;
        }
    }

    void Serial::Run() {
        if (this->is_open()) {
            m_is_run = true;
            parameter _parameter;
            _parameter.fd = this->fd;
            _parameter.id = tty_num;
            _parameter.bind_function = this->call_function;
            _parameter.head = this->head;

            auto *p_message = new Message(_parameter.head, 2);
            p_message->BindPackageCheck(this->check_function);
            messages.push_back(p_message);
//            new std::thread([&]() {
            fd_set rfds;
            struct timeval _timeval;

            while (true) {
                char *serial_buffer = new char[_parameter.read_buffer_size];

                FD_ZERO(&rfds);
                FD_SET(_parameter.fd, &rfds);
                int retval = select(_parameter.fd + 1, &rfds, NULL, NULL, &_timeval);
                if (retval == -1)
                    exit(-1);
                else {
                    if (retval) {
//                            std::unique_lock<std::mutex> mu_read_write_port_lock(mu_read_write_port);//lock
                        int nread = read(_parameter.fd, serial_buffer, 1);
//                            mu_read_write_port_lock.unlock();//ublock
                        if (nread >= 0) {
                            messages[_parameter.id]->SerialRecive((serial_buffer[0]));
                            if (!messages[_parameter.id]->IsEmpty()) {
                                std::vector<char> data_package = messages[_parameter.id]->pop_message();
                                _parameter.bind_function((void *) &_parameter.fd, data_package);
                            }
                        }
                    }
                }

                delete[] serial_buffer;
            }
//            });
            tty_num += 1;
        }
    }

    int Serial::release() {
        if (this->is_open()) {
            return 1;
        }
        return 0;
    }

    void Serial::lock() {
        if (this->is_open()) {

        }
    }

    bool Serial::is_open() const {
        return (this->fd != -1);
    }

    bool Serial::is_run() {
        return m_is_run;
    }

    Serial::~Serial() {

    }

}
#endif
